package ch.ethz.fcl.metrobuzz.data.scene.spacetime;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.vecmath.Vector3f;

import org.lwjgl.opengl.GL11;

import ch.ethz.fcl.metrobuzz.data.scene.MBScene;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Edge;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Node;
import ch.ethz.fcl.metrobuzz.data.singapore.SGMRTStations;
import ch.ethz.fcl.metrobuzz.spatialindex.object3d.BoundingBox;
import ch.ethz.fcl.metrobuzz.spatialindex.object3d.IObject3D;
import ch.ethz.fcl.metrobuzz.spatialindex.object3d.Point3D;
import ch.ethz.fcl.metrobuzz.tools.MBTools;

public class Trip {
	private Itinerary iti;
	private List<TripSegment> tripSegments;

	private long tripId;

	public Trip(Itinerary iti, long tripId) {
		this.iti = iti;
		this.tripId = tripId;
		tripSegments = new ArrayList<TripSegment>();
	}

	public Itinerary getItinerary() {
		return iti;
	}

	public long getTripId() {
		return tripId;
	}

	public void addTripSegment(TripSegment ts, int segmentId) {
		if (segmentId == -1 || segmentId > tripSegments.size())
			tripSegments.add(ts);
		else
			tripSegments.add(segmentId, ts);
	}

	public List<TripSegment> getTripSegments() {
		return tripSegments;
	}

	public class TripSegment implements IObject3D {
		private int[] nodes;
		private float tdep;
		private float tarr;
		private Node depNode;
		private Node arrNode;

		private Trip trip;
		private float probability = 1;

		public TripSegment(MBScene scene, Trip trip, float tdep, float tarr,
				List<Integer> nodes, float probability) {
			this.trip = trip;
			this.tdep = tdep;
			this.tarr = tarr;
			this.nodes = new int[nodes.size()];
			this.probability = probability;

			for (int i = 0; i < nodes.size(); ++i)
				this.nodes[i] = nodes.get(i);

			depNode = scene.getNode(this.nodes[0]);

			arrNode = scene.getNode(this.nodes[nodes.size() - 1]);

			if (depNode == null || arrNode == null)
				System.out.println(this.nodes[0] + " "
						+ this.nodes[nodes.size() - 1]);

			// if the trip is on MRT
			if (SGMRTStations.isMRTStation(depNode.getId())) {
				addMRTTrips(scene, tdep, tarr, nodes);
			}
		}

		private void addMRTTrips(MBScene scene, float tdep, float tarr,
				List<Integer> nodes) {
			for (int i = 0; i < nodes.size(); i++) {
				Node node = scene.getNode(nodes.get(i));

				// add node passing time
				float passTime = tdep + (tarr - tdep) / (nodes.size() - 1) * i;
				node.addTrajPassTime(this, passTime);

				// add edge passing time
				if (i != nodes.size() - 1) {
					Node nextNode = scene.getNode(nodes.get(i + 1));

					if (node == nextNode)
						continue;
					// System.out.println("Start " + node.getId() + " next "
					// + nextNode.getId());
					Edge edge = scene.getEdge(node, nextNode);

					// System.out.println("edge " + edge.getId() + " start "
					// + node.getId() + " end " + nextNode.getId());
					// every 15 minutes interval
					int timeInterval = (int) (passTime / 900);

					edge.addPassTrajIds(timeInterval, this);
				}
			}
			scene.addMRTTripSize();
		}

		public Point3D getNodeWithTime(MBScene scene, int index) {
			if (index >= nodes.length) {
				return null;
			}

			float delta = (tarr - tdep) / (nodes.length - 1);
			Node node = scene.getNode(nodes[index]);
			return new Point3D(node.getX(), node.getY(), tdep + delta * index);
		}

		public int[] getNodesIndices() {
			return nodes;
		}

		public float getDepTime() {
			return tdep;
		}

		public void setDepTime(float tdep) {
			this.tdep = tdep;
		}

		public float getArrTime() {
			return tarr;
		}

		public void setArrTime(float tarr) {
			this.tarr = tarr;
		}

		public Node getDepNode() {
			return depNode;
		}

		public Node getArrNode() {
			return arrNode;
		}

		public float getProbability() {
			return probability;
		}

		public Point3D getDepTimeLoc() {
			float x = depNode.getX();
			float y = depNode.getY();
			float z = this.getDepTime();
			return new Point3D(x, y, z);
		}

		public Point3D getArrTimeLoc() {
			float x = arrNode.getX();
			float y = arrNode.getY();
			float z = this.getArrTime();
			return new Point3D(x, y, z);
		}

		public void print() {
			for (int i = 0; i < nodes.length; i++)
				System.out.print(nodes[i] + " ");
			System.out.println();
		}

		@Override
		public BoundingBox getBoundingBox() {
			return null;
		}

		@Override
		public boolean intersects(BoundingBox bb) {
			return false;
		}

		@Override
		public boolean contains(BoundingBox bb) {
			return false;
		}

		public Point3D getLocTime(MBScene scene, float t) {
			// System.out.println("" + this + " t = " + t);
			if (t > tarr || t < tdep)
				return null;
			float delta = (tarr - tdep) / (nodes.length - 1);
			float currt = tdep;
			for (int i = 0; i < nodes.length - 1; i++) {
				// System.out.print("Trip: " + this + " currt = " + currt +
				// " t=" +
				// t);
				if (t >= currt && t <= currt + delta) {
					float nodeT = t - currt;
					float percT = MBTools.map(nodeT, 0, delta, 0, 1);

					Node n1 = scene.getNode(nodes[i]);
					Node n2 = scene.getNode(nodes[i + 1]);
					Vector3f v1 = new Vector3f(n1.getX(), n1.getY(), 0f);
					Vector3f v2 = new Vector3f(n2.getX(), n2.getY(), 0f);
					v2.sub(v1);
					v2.scale(percT);
					v1.add(v2);
					// System.out.println("percT: " + percT + " n1: " +
					// n1.getX() +
					// "," + n1.getY() + " n2: " + n2.getX() + "," + n2.getY() +
					// " v1: " + v1.x + "," + v1.y);
					return new Point3D(v1.x, v1.y, t);
				}
				currt += delta;
			}
			return null;
		}

		public Trip getTrip() {
			return trip;
		}

		@Override
		public void render() {
		}

		public void render(MBScene scene) {
			GL11.glBegin(GL11.GL_LINE_STRIP);
			Random rand = new Random(10);
			for (int i = 0; i < nodes.length; i++) {
				Point3D loc = this.getNodeWithTime(scene, i);
				if (loc == null) {
					System.out.println("loc == null at index " + i);
				} else
					// GL11.glVertex3f(loc.getX(), loc.getY(), loc.getZ());
					GL11.glVertex3d(loc.getX() + rand.nextFloat() / 100,
							loc.getY() + rand.nextFloat() / 100, 0);
			}
			GL11.glEnd();
		}
	}
}
